/* Copyright (c) 2011, Peter Barrett
 * Copyright (c) 2017-2018, Alexander Pruss
**
** Permission to use, copy, modify, and/or distribute this software for
** any purpose with or without fee is hereby granted, provided that the
** above copyright notice and this permission notice appear in all copies.
**
** THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
** WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
** WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR
** BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES
** OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
** WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
** ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
** SOFTWARE.
*/

#include "USBComposite.h" 

#include <string.h>
#include <stdint.h>
#include <libmaple/nvic.h>
#include "usb_hid.h"
#include "usb_composite_serial.h"
#include "usb_generic.h"
#include <libmaple/usb.h>
#include <string.h>
#include <libmaple/iwdg.h>
#include <DEBUG.h>
#include <wirish.h>

/*
 * USB HID interface
 */

bool USBHID::init(USBHID* me) {
    usb_hid_setTXEPSize(me->txPacketSize);
	return true;
}

bool USBHID::registerComponent() {
	return USBComposite.add(&usbHIDPart, this, (USBPartInitializer)&USBHID::init);
}

void USBHID::setReportDescriptor(const uint8_t* report_descriptor, uint16_t report_descriptor_length) {
	usb_hid_set_report_descriptor(report_descriptor, report_descriptor_length);
}

void USBHID::setGetReportDescrCallback(TGetReportDescr callback){
	usb_hid_set_getreport_descriptor_callback(callback);
}



void USBHID::setReportDescriptor(const HIDReportDescriptor* report) {
    setReportDescriptor(report->descriptor, report->length);
}

bool USBHID::begin(const uint8_t* report_descriptor, uint16_t report_descriptor_length) {

	setReportDescriptor(report_descriptor, report_descriptor_length);
	
	USBComposite.clear();
	registerComponent();
	enabledHID = true;
	return USBComposite.begin(); 
	
	
}

bool USBHID::begin(const HIDReportDescriptor* report) {
    return begin(report->descriptor, report->length);
}

void USBHID::setBuffers(uint8_t type, volatile HIDBuffer_t* fb, int count) {
    usb_hid_set_buffers(type, fb, count);
}

bool USBHID::addBuffer(uint8_t type, volatile HIDBuffer_t* buffer) {
    return 0 != usb_hid_add_buffer(type, buffer);
}

void USBHID::clearBuffers(uint8_t type) {
	usb_hid_clear_buffers(type);
}

void USBHID::clearBuffers() {
	clearBuffers(HID_REPORT_TYPE_OUTPUT);
	clearBuffers(HID_REPORT_TYPE_FEATURE);
}

bool USBHID::end(void){
	enabledHID = false;
	return USBComposite.end();
}

bool USBHID::begin(USBCompositeSerial serial, const uint8_t* report_descriptor, uint16_t report_descriptor_length) {	
	USBComposite.clear();

	setReportDescriptor(report_descriptor, report_descriptor_length);
	registerComponent();

	serial.registerComponent();
	enabledHID = true;
	return USBComposite.begin();
}

bool USBHID::IsConfigured(){
	return USBComposite.IsConfigured();
}

bool USBHID::begin(USBCompositePart* parts,const uint8* devDescr,uint16 devDescrSize,const uint8* confDescr,uint16 confDescrSize,const uint8* iManufacturer, const uint8* iProduct, const uint8* iSerialNumber,TGetReportDescr callback) {	
	USBComposite.clear();
	setGetReportDescrCallback(callback);
	usb_hid_set_hid_part(parts);
	USBComposite.add(parts, this, (USBPartInitializer)&USBHID::init);
	enabledHID = true;
	return USBComposite.begin(devDescr,devDescrSize,confDescr,confDescrSize,iManufacturer,iProduct,iSerialNumber);
	
}

bool USBHID::begin(USBCompositePart* parts,const uint8* devDescr,uint16 devDescrSize,const uint8* confDescr,uint16 confDescrSize,const uint8* iManufacturer, const uint8* iProduct, const uint8* iSerialNumber,const uint8_t* report_descriptor, uint16_t report_descriptor_length) {	
	USBComposite.clear();
	usb_hid_set_hid_part(parts);
	setReportDescriptor(report_descriptor, report_descriptor_length);
	USBComposite.add(parts, this, (USBPartInitializer)&USBHID::init);
	enabledHID = true;
	return USBComposite.begin(devDescr,devDescrSize,confDescr,confDescrSize,iManufacturer,iProduct,iSerialNumber);
}

bool USBHID::begin(USBCompositePart* parts,const uint8* devDescr,uint16 devDescrSize,const uint8* confDescr,uint16 confDescrSize,const uint8* iManufacturer, const uint8* iProduct, const uint8* iSerialNumber,const HIDReportDescriptor* report) {	
	USBComposite.clear();
	usb_hid_set_hid_part(parts);
	setReportDescriptor(report->descriptor, report->length);
	USBComposite.add(parts, this, (USBPartInitializer)&USBHID::init);
	enabledHID = true;
	return USBComposite.begin(devDescr,devDescrSize,confDescr,confDescrSize,iManufacturer,iProduct,iSerialNumber);
}


bool USBHID::begin(USBCompositeSerial serial, const HIDReportDescriptor* report) {
    return begin(serial, report->descriptor, report->length);
}

void HIDReporter::sendReport(uint8 ep) {
//    while (usb_is_transmitting() != 0) {
//    }

    unsigned toSend = bufferSize;
    uint8* b = buffer;
   
	//DEBUG("sendReport ep[%d] buffer:",ep);
	//DEBUG_B(buffer,toSend);
	
    while (toSend) {
		unsigned delta=0;
		if(ep==0){
			//DEBUG("usb_hid_tx delta=%d,toSend=%d",delta,toSend);
			delta=usb_hid_tx(b, toSend);
		}
		else{
			//DEBUG("ep=%d,usb_hid_m_tx delta=%d,b=%p,toSend=%d",ep,delta,b,toSend);
			delta=usb_hid_m_tx(ep,b,toSend);
			
		}
		//DEBUG("delta=%d,toSend=%d",delta,toSend);
      //unsigned delta= usb_hid_tx(b, toSend);
        toSend -= delta;
        b += delta;
		
    }
    
//    while (usb_is_transmitting() != 0) {
//    }

    /* flush out to avoid having the pc wait for more data */
	//DEBUG("usb_hid_m_tx0----1",1);
	if(ep==0){
		usb_hid_tx(NULL, 0);
	}
	else{
		usb_hid_m_tx(ep,NULL,0);
	}
	//DEBUG("usb_hid_m_tx0-----2",1);
}
        
HIDReporter::HIDReporter(USBHID& _HID, uint8_t* _buffer, unsigned _size, uint8_t _reportID) : HID(_HID) {
    if (_reportID == 0) {
        buffer = _buffer+1;
        bufferSize = _size-1;
    }
    else {
        buffer = _buffer;
        bufferSize = _size;
    }
    memset(buffer, 0, bufferSize);
    reportID = _reportID;
    if (_size > 0 && reportID != 0)
        buffer[0] = _reportID;
}

HIDReporter::HIDReporter(USBHID& _HID, uint8_t* _buffer, unsigned _size) : HID(_HID) {
    buffer = _buffer;
    bufferSize = _size;
    memset(buffer, 0, _size);
    reportID = 0;
}

void HIDReporter::setFeature(uint8_t* in) {
    return usb_hid_set_feature(reportID, in);
}

uint16_t HIDReporter::getData(uint8_t type, uint8_t* out, uint8_t poll) {
    return usb_hid_get_data(type, reportID, out, poll);
}

uint16_t HIDReporter::getFeature(uint8_t* out, uint8_t poll) {
    return usb_hid_get_data(HID_REPORT_TYPE_FEATURE, reportID, out, poll);
}

uint16_t HIDReporter::getOutput(uint8_t* out, uint8_t poll) {
    return usb_hid_get_data(HID_REPORT_TYPE_OUTPUT, reportID, out, poll);
}

